#include "Index_Table.h"
const char* CHECK_SUM = "End_of_Index";

CIndex_Table::CIndex_Table(void)
{
    initialization();
}

CIndex_Table::~CIndex_Table(void)
{
    delete_index_table();
}

int CIndex_Table::initialization()
{
    // this->capacity = 0;
    this->size = 0;
    this->chosenSeedId = 3;
    this->uiSubDiffThreshold = 3;
    this->uiRead_Length = 0;
    this->bMapReadInColors = false;
    this->pIndexTable = NULL;
    this->pHashIndexTable = NULL;
    this->pbaMaskedFlag = NULL;
    return(0);
}

void CIndex_Table::delete_index_table(void)
{
    delete [] this->pIndexTable;
    delete this->pbaMaskedFlag;
    delete this->pHashIndexTable;
    this->initialization(); // pointer to NULL;
}

void CIndex_Table::printInfo(void) const
{
    LOG_INFO("Info %d: Read length is %u with size %u\n",\
             CONFIG_LOG, this->uiRead_Length, this->size);
    if (this->bMapReadInColors) {
        LOG_INFO("Info %d: This index table is for SOLiD reads. %s\n",\
                 CONFIG_LOG, BLANK_LINE);
    } else {
        LOG_INFO("Info %d: This index table is for Illumina reads. %s\n",\
                 CONFIG_LOG, BLANK_LINE);
    }
}

// Save the index to a file with binary file. filePostfix should indicate which reference genome is preprocessed.
bool CIndex_Table::save_Hash_Table(FILE* fp) const
{
    bool sucessfullySaveTable = true;
    char colorReads = this->bMapReadInColors ? 'Y' : 'N';
    // TODO: write the seed option into index
    // int seedOption = this->chosenSeedId;
    if (this->pIndexTable != NULL) {
        printInfo();
        fprintf(fp, "%u\n%u\n%u\n%c", this->chosenSeedId, this->uiRead_Length, this->size, colorReads);

        // save hash table
        sucessfullySaveTable = myFwrite((void*)this->pIndexTable, sizeof(CIndex_Type), this->size, fp);
        if (!sucessfullySaveTable) ERR;

        if (this->pHashIndexTable != NULL) {
            fprintf(fp, "\n%u\n%u", \
                    this->pHashIndexTable->uiWindowSize, this->pHashIndexTable->uiSize);
            // The Hash Index table has uiSize + 1 record
            sucessfullySaveTable = myFwrite((void*)this->pHashIndexTable->aiIndexTable, \
                                            sizeof(unsigned int), (this->pHashIndexTable->uiSize + 1), fp);
            if (!sucessfullySaveTable) ERR;
        } else {
            ERR;
            sucessfullySaveTable = false;
        }

        // Write math repeat flags
        if (this->pbaRepeatRepresentativeFlag != NULL) {
            fprintf(fp, "\n%u", this->pbaRepeatRepresentativeFlag->size);
            sucessfullySaveTable = myFwrite((void*) this->pbaRepeatRepresentativeFlag->bflag,
                                            sizeof(unsigned char), (this->pbaRepeatRepresentativeFlag->size / 8 + 1), fp);
            if (!sucessfullySaveTable) ERR;
        }
        fprintf(fp, "\n%s\n", CHECK_SUM);
    } else {
        LOG_INFO("Info %d: The Index is empty\n", ERROR_LOG);
        sucessfullySaveTable = false;
    }
    return(sucessfullySaveTable);
}

bool CIndex_Table::read_Hash_Table(FILE* fp)
{
    bool sucessfullyReadTable = true;
    // int seedOption = this->chosenSeedId;
    if (fscanf(fp, "%u\n%u\n%u\n", \
               &this->chosenSeedId, &this->uiRead_Length, &this->size) != 3) {
        ERR; // Read hash table
    }
    // Get a character until Y or N, indicating this is the SOliD reads.
    while (char c = (char)fgetc(fp)) { // This is a check sum of the correctness.
        if (c == 'Y' || c == 'N') {
            this->bMapReadInColors = (c == 'Y');
            LOG_INFO("Info %d: Mapping SOLiD reads (Yes/No): %c%s\n", CONFIG_LOG, c, BLANK_LINE);
            break;
        }
    }
    LOG_INFO("Info %d: Anchor length: %u, Size: %u\n", CONFIG_LOG, this->uiRead_Length, this->size);

    if (this->pIndexTable == NULL) {
        this->pIndexTable = new CIndex_Type[this->size];
        if (this->pIndexTable == NULL) ERR; // fail to new space for table
    } else
        LOG_INFO("Info %d: HashTable was not NULL\n", WARNING_LOG);

    sucessfullyReadTable = myFread((void*)this->pIndexTable, sizeof(CIndex_Type), this->size, fp);
    if (!sucessfullyReadTable) {
        LOG_INFO("Info %d: Fail to read index.\n", ERROR_LOG);
        delete this->pIndexTable;
        this->pIndexTable = NULL;
        return(!sucessfullyReadTable);
    }

    unsigned int uiWindowSize = 0, uiHashIndexTableSize = 0;
    if (fscanf(fp, "\n%u\n%u", &(uiWindowSize), &(uiHashIndexTableSize)) <=0) {
        ERR;
    }
    LOG_INFO("Info %d: Take the first %u bases for hashing(bucketing)\n", FINE_LOG, uiWindowSize);

    if (uiHashIndexTableSize > 0) {
        if (this->pHashIndexTable == NULL) {
            this->pHashIndexTable = new CHashIndexT(uiHashIndexTableSize); //Note there are one more counter
        }
        if (this->pHashIndexTable == NULL) {
            ERR; // Fail to new pHashIndexTable
            return(!sucessfullyReadTable);
        }
        sucessfullyReadTable = myFread((void*)this->pHashIndexTable->aiIndexTable,\
                                       sizeof(unsigned int), (this->pHashIndexTable->uiSize + 1), fp);
        if (!sucessfullyReadTable) ERR;
    } else {
        ERR;
    }
    // Read math repeat flags
    unsigned int numOfFlags = 0;
    if (fscanf(fp, "\n%u", &(numOfFlags)) <=0 ) {
        ERR;
    }
    if (this->pbaRepeatRepresentativeFlag == NULL) {
        this->pbaRepeatRepresentativeFlag = new CboolFlagArray(numOfFlags);
    }
    sucessfullyReadTable = myFread((void*)this->pbaRepeatRepresentativeFlag->bflag,
                                   sizeof(unsigned char), (this->pbaRepeatRepresentativeFlag->size / 8 + 1), fp);
    if (!sucessfullyReadTable || !assertFile(fp, CHECK_SUM)) {
        ERR;
    }
    return(sucessfullyReadTable);
}

string default_index_path(string filePostfix, bool bColorReads,\
                          unsigned int seedOption, unsigned int uiReadLength)
{
    char fileName[MAX_LINE];
    const char* colorReads = bColorReads ? "SOLiD" : "Illumina";
    sprintf(fileName, "%u_%u_%s_%s_v2.index", \
            uiReadLength, seedOption, colorReads, filePostfix.c_str());
    return(string(fileName));
}
